package com.coscon.model;


import com.coscon.enums.PlayerToolSoftEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import oshi.SystemInfo;
import oshi.software.os.OSProcess;
import oshi.software.os.OperatingSystem;

import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.*;
import java.util.regex.Pattern;


/**
 * Liste et informations sur les process linux ou unix.   获取进程信息
 * @author Emeric Vernat
 */
@Slf4j
public final class ProcessInformations implements Serializable {
    private static final long serialVersionUID = 2163916067335213382L;
    private static final Pattern WINDOWS_STATE_PATTERN = Pattern.compile("................");
    private static final Pattern WINDOWS_CPU_TIME_PATTERN = Pattern.compile("[0-9:]*");

    private String user = null;
    private int pid = -1;
    // rapport Temps CPU / Temps utilisateur.
    // Il s'agit d'un rapport entre le temps d'exécution effectif et le temps depuis lequel le processus a été lancé
    private float cpuPercentage = 0;
    private float memPercentage = 0;
    // taille virtuelle de l'image du processus (code + données + pile).
    private int vsz = -1;
    // taille résidente de l'image du processus. Nombre de kilo-octets se trouvant en mémoire
    private int rss = -1;
    // numéro mineur de périphérique tty (terminal de contrôle)
    private String tty = null;
    // état du processus.  Le premier champ correspond à
    // R (runnable) prêt à  être exécuté,
    // S (sleeping) endormi,
    // D sommeil ininterruptible,
    // T (traced) arrêté ou suivi,
    // Z (zombie).
    // Le second champ contient W si le processus n'a pas de pages résidentes.
    // Le troisième champ contient N si le processus a une valeur de gentillesse positive (nice, champ NI).
    private String stat = null;
    private String start = null;
    private String cpuTime = null;
    private String command = null;
    private String name = null;
    private String path = null;

    public ProcessInformations(){

    }

    private ProcessInformations(Scanner sc, boolean windows, boolean macOrAix) {

        super();
        if (windows) {
            final StringBuilder imageNameBuilder = new StringBuilder(sc.next());
            while (!sc.hasNextInt()) {
                imageNameBuilder.append(' ').append(sc.next());
            }
            pid = sc.nextInt();
            if ("Console".equals(sc.next())) {
                // ce if est nécessaire si windows server 2003 mais sans connexion à distance ouverte
                // (comme tasklist.txt dans resources de test,
                // car parfois "Console" est présent mais parfois non)
                sc.next();
            }
            final String memory = sc.next();
            cpuPercentage = -1;
            memPercentage = -1;
            vsz = Integer.parseInt(memory.replace(".", "").replace(",", "").replace("ÿ", ""));
            rss = -1;
            tty = null;
            sc.next();
            sc.skip(WINDOWS_STATE_PATTERN);
            stat = null;
            final StringBuilder userBuilder = new StringBuilder(sc.next());
            while (!sc.hasNext(WINDOWS_CPU_TIME_PATTERN)) {
                userBuilder.append(' ').append(sc.next());
            }
            this.user = userBuilder.toString();
            start = null;
            cpuTime = sc.next();
            command = imageNameBuilder.append("   (").append(sc.nextLine().trim()).append(')')
                    .toString();
        } else {
            user = sc.next();
            pid = sc.nextInt();
            cpuPercentage = Float.parseFloat(sc.next().replace(",", "."));
            memPercentage = Float.parseFloat(sc.next().replace(",", "."));
            vsz = sc.nextInt();
            rss = sc.nextInt();
            tty = sc.next();
            stat = sc.next();
            if (macOrAix && sc.hasNextInt()) {
                start = sc.next() + ' ' + sc.next();
            } else {
                start = sc.next();
            }
            cpuTime = sc.next();
            command = sc.nextLine();
        }
    }

    public static List<ProcessInformations> buildProcessInformations(InputStream in,
                                                                     boolean windows, boolean macOrAix) {
        final String charset;
        if (windows) {
            charset = "GB2312";//window 环境的编码
        } else {
            charset = "UTF-8";
        }
        final Scanner sc = new Scanner(in, charset);
        sc.useRadix(10);
        sc.useLocale(Locale.US);
        if (windows) {
//            sc.nextLine();
//            sc.nextLine();
        }

        final List<ProcessInformations> processInfos = new ArrayList<>();
        while (sc.hasNext()) {
            final ProcessInformations processInfo = new ProcessInformations(sc, windows, macOrAix);
            processInfos.add(processInfo);
        }
        return Collections.unmodifiableList(processInfos);
    }

    public static List<ProcessInformations> buildProcessInformations() throws IOException {
        List<ProcessInformations> list = new ArrayList<>();
        SystemInfo si = new SystemInfo();
        OperatingSystem os = si.getOperatingSystem();
        List<OSProcess> procs = os.getProcesses();
        for(OSProcess p : procs){
            ProcessInformations processInformations = new ProcessInformations();
            processInformations.setName(p.getName());
            processInformations.setCommand(p.getCommandLine());
            processInformations.setPath(p.getPath());
            processInformations.setPid(p.getProcessID());
            processInformations.setStart(DateFormatUtils.format(p.getStartTime(),"yyyy-MM-dd HH:mm:ss") );
            processInformations.setUser(p.getUser());
            list.add(processInformations);
        }
        return list;


//        Process process = null;
//        try {
//            // pour nodes Jenkins, on évalue ces propriétés à chaque fois sans utiliser de constantes
//            final String osName = System.getProperty("os.name").toLowerCase(Locale.ENGLISH);
//            final boolean windows = osName.contains("windows");
//            final boolean mac = osName.contains("mac");
//            final boolean aix = osName.contains("aix");
//            if (windows) {
//                process = Runtime.getRuntime().exec(new String[] { "cmd", "/c", "tasklist /V" });
//            } else if (mac || aix) {
//                // le "f" de "ps wauxf" n'est pas supporté sur Mac OS X et sur AIX, cf issues 74 et 99
//                process = Runtime.getRuntime().exec(new String[] { "/bin/sh", "-c", "ps waux" });
//            } else {
//                // tous les systèmes (ou presque) non Windows sont une variante de linux ou unix
//                // (http://mindprod.com/jgloss/properties.html) qui acceptent la commande ps
//                process = Runtime.getRuntime().exec(new String[] { "/bin/sh", "-c", "ps wauxf" });
//            }
//            return buildProcessInformations(process.getInputStream(), windows, mac || aix);
//        } finally {
//            if (process != null) {
//                // évitons http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6462165
//                process.getInputStream().close();
//                process.getOutputStream().close();
//                process.getErrorStream().close();
//                process.destroy();
//            }
//        }
    }


    //搜索进程信息
    //VLC|QQ|AppleCredentialManagerDaemon|init|maven
    public static List<ProcessInformations> searchProcessInformations(String searchKey) throws IOException {
        Process process = null;
        try {
            // pour nodes Jenkins, on évalue ces propriétés à chaque fois sans utiliser de constantes
            final boolean windows = SystemUtils.IS_OS_WINDOWS;
            final boolean mac = SystemUtils.IS_OS_MAC;
            final boolean aix = SystemUtils.IS_OS_UNIX;
            if (windows) {
                String str = "tasklist /V|findstr \""+searchKey+"\"";
                System.out.println(str);
                process = Runtime.getRuntime().exec(new String[] { "cmd", "/c",str });
            } else if (mac || aix) {
                // le "f" de "ps wauxf" n'est pas supporté sur Mac OS X et sur AIX, cf issues 74 et 99
                String cmd = "ps waux|grep -E \""+searchKey+"\" | grep -v grep";
                System.out.println("执行命令是：" + cmd);
                process = Runtime.getRuntime().exec(new String[] { "/bin/sh", "-c", cmd });
            } else {
                // tous les systèmes (ou presque) non Windows sont une variante de linux ou unix
                // (http://mindprod.com/jgloss/properties.html) qui acceptent la commande ps
                process = Runtime.getRuntime().exec(new String[] { "/bin/sh", "-c", "ps wauxf" });
            }
            return buildProcessInformations(process.getInputStream(), windows, mac || aix);
        } finally {
            if (process != null) {
                // évitons http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6462165
                process.getInputStream().close();
                process.getOutputStream().close();
                process.getErrorStream().close();
                process.destroy();
            }
        }
    }

    /**
     * 判断某个进程是否运行
     * @param toolSoftEnum
     * @return
     */
    public static boolean processIsRun(PlayerToolSoftEnum toolSoftEnum){
        boolean flag = false;//默认没有运行
        if(toolSoftEnum==null){
           return flag;
        }
        long s = System.currentTimeMillis();
        if(SystemUtils.IS_OS_WINDOWS){//如果系统win，则这么处理
            SystemInfo si = new SystemInfo();
            OperatingSystem os = si.getOperatingSystem();
            List<OSProcess> procs = os.getProcesses();
            for(OSProcess p : procs){
                if(StringUtils.containsIgnoreCase(p.getPath(), toolSoftEnum.getProcessName())){
                    flag = true;
                    break;
                }
            }
        }else{//如果是mac， linux 则采用ps搜索
            try {
                List<ProcessInformations> list = searchProcessInformations(toolSoftEnum.getProcessName());
                if(list!=null && !list.isEmpty()){
                    flag = true;//标记进程运行中
                }
            } catch (IOException e) {
                log.debug(e.getMessage());
                flag = false;
            }
        }
        long e = System.currentTimeMillis();
        System.out.println("花费时间：" +(e-s) );
        return flag;
    }

    @Override
    public String toString() {
        return "ProcessInformations{" +
                "user='" + user + '\'' +
                ", pid=" + pid +
                ", vsz=" + vsz +
                ", rss=" + rss +
                ", tty='" + tty + '\'' +
                ", stat='" + stat + '\'' +
                ", start='" + start + '\'' +
                ", cpuTime='" + cpuTime + '\'' +
                ", command='" + command + '\'' +
                '}';
    }

    public String getUser() {
        return user;
    }

    public int getPid() {
        return pid;
    }

    public float getCpuPercentage() {
        return cpuPercentage;
    }

    public float getMemPercentage() {
        return memPercentage;
    }

    public int getVsz() {
        return vsz;
    }

    public int getRss() {
        return rss;
    }

    public String getTty() {
        return tty;
    }

    public String getStat() {
        return stat;
    }

    public String getStart() {
        return start;
    }

    public String getCpuTime() {
        return cpuTime;
    }

    public String getCommand() {
        return command;
    }

    public void setUser(String user) {
        this.user = user;
    }

    public void setPid(int pid) {
        this.pid = pid;
    }

    public void setCpuPercentage(float cpuPercentage) {
        this.cpuPercentage = cpuPercentage;
    }

    public void setMemPercentage(float memPercentage) {
        this.memPercentage = memPercentage;
    }

    public void setVsz(int vsz) {
        this.vsz = vsz;
    }

    public void setRss(int rss) {
        this.rss = rss;
    }

    public void setTty(String tty) {
        this.tty = tty;
    }

    public void setStat(String stat) {
        this.stat = stat;
    }

    public void setStart(String start) {
        this.start = start;
    }

    public void setCpuTime(String cpuTime) {
        this.cpuTime = cpuTime;
    }

    public void setCommand(String command) {
        this.command = command;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public static void main(String[] args) throws InterruptedException, IOException {
//        List<ProcessInformations> list ;
////                list =    ProcessInformations.buildProcessInformations();
//        list =    ProcessInformations.searchProcessInformations("MacOS/VLC");
//        for(ProcessInformations p : list){
//            System.out.println(p);
//        }
//
//        list =    ProcessInformations.searchProcessInformations("MacOS/XLPlayer");
//        for(ProcessInformations p : list){
//            System.out.println(p);
//        }

        boolean vlcIsRun = ProcessInformations.processIsRun(PlayerToolSoftEnum.POT_PLAYER_WIN);
        System.out.println(vlcIsRun);

    }


}